#include "config.h"
#include "cache.h"
#include "parser.h"
#include "queue.h"
#include <fstream>
#include <assert.h>

unsigned long cur_cyc = 0;

bool Private_Finished(L1Cache** L1, L2Cache** L2, Memory* Mem, Parser** parser, int core_num) {
  bool rc = 1;
  if (!Mem->QEmpty())
    return 0;
  for(int i=0; i<core_num; i++) {
    rc &= L1[i]->QEmpty();
    rc &= L2[i]->QEmpty();
    rc &= parser[i]->QEmpty();
  }
  return rc;
}

bool Shared_Finished(L1Cache** L1, L2Cache* L2, Memory* Mem, Parser** parser, int core_num) {
  bool rc = 1;
  if (!Mem->QEmpty() || !L2->QEmpty())
    return 0;
  for(int i=0; i<core_num; i++) {
    rc &= L1[i]->QEmpty();
    rc &= parser[i]->QEmpty();
  }
  return rc;
}

int sharedL2_main(int argc, char* argv[]) {
  int core_num = atoi(argv[1]);
  int L1_size = atoi(argv[2]);
  int L2_size = atoi(argv[3]);
  int file_index = 4+core_num;
  unsigned long total_access = 0;
  unsigned long total_hit = 0;

  // Create modules in the simulator
    // Create L1
  L1Cache** L1 = new L1Cache*[core_num];
  for(int i=0; i<core_num; i++)
    L1[i] = new L1Cache(L1_size, L1_BLOCK_SIZE, L1_LATENCY, L1_ASSOCIATIVITY, i);
    // Create L2
  L2Cache* L2 = new L2Cache(L2_size, L2_BLOCK_SIZE, L2_LATENCY, L2_ASSOCIATIVITY,
    16, core_num, 0, core_num-1);

    // Create Memory
  Memory* Mem = new Memory(core_num, core_num, 99);
    // Create Parser
  Parser** parser = new Parser*[core_num];
  for(int i=0; i<core_num; i++) {
    parser[i] = new Parser(argv[file_index+i], i);
  }

  // Connect modules in the simulator
  for(int i=0; i<core_num; i++) {
    L1[i]->SetL2(L2->Q);
    L2->SetL1(i, L1[i]->Q);
    parser[i]->SetL1(L1[i]->Q);
  }
  L2->SetMem(Mem->Q);
  Mem->SetL2(core_num, L2->Q);

  while(!Shared_Finished(L1, L2, Mem, parser, core_num)) {
    cur_cyc++;
    Mem->Update();
    L2->Update();
    for(int i=0; i<core_num; i++) {
      L1[i]->Update();
      parser[i]->Update();
    }
  }

  for(int i=0; i<core_num; i++) {
    L1[i]->PrintStats();
    total_hit += L1[i]->hit;
  }
  cout<<"=================================================="<<endl;
  L2->PrintStats();
  cout<<endl<<endl;

  return 0;
}

int privateL2_main(int argc, char* argv[]) {
  int core_num = atoi(argv[1]);
  int L1_size = atoi(argv[2]);
  int L2_size = atoi(argv[3]);
  int* L2_banks = new int[core_num];
  for(int i=4; i<4+core_num; i++)
    L2_banks[i-4] = atoi(argv[i]);
  int file_index = 4+core_num;
  unsigned long total_access = 0;
  unsigned long total_hit = 0;

  // Create modules in the simulator
    // Create L1
  L1Cache** L1 = new L1Cache*[core_num];
  for(int i=0; i<core_num; i++)
    L1[i] = new L1Cache(L1_size, L1_BLOCK_SIZE, L1_LATENCY, L1_ASSOCIATIVITY, i);
    // Create L2
  L2Cache** L2 = new L2Cache*[core_num];
  for(int i=0; i<core_num; i++)
    L2[i] = new L2Cache(L2_size, L2_BLOCK_SIZE, L2_LATENCY, L2_ASSOCIATIVITY,
        L2_banks[i], i+core_num, i, i);

    // Create Memory
  Memory* Mem = new Memory(core_num, core_num, 99);

    // Create Parser
  Parser** parser = new Parser*[core_num];
  for(int i=0; i<core_num; i++) {
    parser[i] = new Parser(argv[file_index+i], i);
  }

  // Connect modules in the simulator
  for(int i=0; i<core_num; i++) {
    L1[i]->SetL2(L2[i]->Q);
    L2[i]->SetL1(i, L1[i]->Q);
    L2[i]->SetMem(Mem->Q);
    Mem->SetL2(i+core_num, L2[i]->Q);
    parser[i]->SetL1(L1[i]->Q);
  }

  while(!Private_Finished(L1, L2, Mem, parser, core_num)) {
    cur_cyc++;
    Mem->Update();
    //cout<<"Mem"<<endl;
    for(int i=0; i<core_num; i++) {
      L2[i]->Update();
      //cout<<"L2"<<endl;
      L1[i]->Update();
      //cout<<"L1"<<endl;
      parser[i]->Update();
      //cout<<"Parser"<<endl;
    }
  }

  for(int i=0; i<core_num; i++) {
    L1[i]->PrintStats();
    L2[i]->PrintStats();
    total_hit += L1[i]->hit;
  }

  cout<<endl<<endl;

  return 0;
}

int main(int argc, char* argv[]) {
  int core_num = atoi(argv[1]);
  multi_test = 1;
  if (core_num>1 && atoi(argv[4]) == 16)
    sharedL2_main(argc, argv);
  else
    privateL2_main(argc, argv);
}

